<?php

/**
 * SimplifyPHP Framework
 *
 * This file is part of SimplifyPHP Framework.
 *
 * SimplifyPHP Framework is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * SimplifyPHP Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author Rodrigo Rutkoski Rodrigues, <rutkoski@gmail.com>
 */

/**
 *
 * @author "Rodrigo Rutkoski Rodrigues, <rutkoski@gmail.com>"
 *
 */
class MDB2_QueryObject extends QueryObject
{

  /**
   *
   * @var MDB2_Statement_Common
   */
  public $stmt;

  /**
   *
   * @var MDB2_Result_Common
   */
  public $res;

  /**
   *
   * @var string
   */
  public $lastQuery;

  /**
   * Constructor. Create a new query object using MDB2.
   *
   * @param MDB2_Driver_Common $mdb2
   * @return void
   */
  public function __construct(MDB2_DAO $dao)
  {
    parent::__construct($dao);
  }

  /**
   * Free last statement
   *
   * @return MDB2_QueryObject
   */
  public function free()
  {
    if (! empty($this->stmt)) {
      $this->stmt->free();
      $this->stmt = null;
    }
    return $this;
  }

  public function executeRaw()
  {
    $query = $this->buildQuery();

    $query = MDB2_DAO::processQuery($query, $this->getConfig());

    $this->lastQuery = $query;

    $this->res = $this->db()->query($query);
    MDB2_DAO::validate($this->res, $query);

    return $this;
  }

  /**
   * Prepare (if not prepared) and execute statement.
   *
   * @param array $data data that will be passed to the prepared statement
   * @return MDB2_QueryObject
   */
  public function execute($data = null)
  {
    $query = $this->buildQuery();

    $query = MDB2_DAO::processQuery($query, $this->getConfig());

    if (empty($this->stmt) || $this->lastQuery != $query) {
      if (! is_null($this->limit) || ! is_null($this->offset)) {
        $this->db()->setLimit($this->limit, $this->offset);
        MDB2_DAO::validate($this->db(), $query);
      }

      if (strpos($query, 'SELECT') === 0) {
        $this->stmt = $this->db()->prepare($query);
      }
      else {
        $this->stmt = $this->db()->prepare($query, null, MDB2_PREPARE_MANIP);
      }

      MDB2_DAO::validate($this->stmt, $query);
    }

    $this->lastQuery = $query;

    if (! is_null($data) && ! is_array($data) && func_num_args() > 1) {
      $data = func_get_args();
    }

    $this->res = $this->stmt->execute($data);
    MDB2_DAO::validate($this->res, $query);

    return $this;
  }

  /**
   * Get the number of rows in result set
   *
   * @return int
   */
  public function numRows()
  {
    if (is_int($this->res)) {
      $return = $this->res;
    }
    else {
      $return = $this->res->numRows();
      MDB2_DAO::validate($return);
    }

    return $return;
  }

  /**
   * Get the number of columns in result set
   *
   * @return int
   */
  public function numCols()
  {
    $return = $this->res->numCols();
    MDB2_DAO::validate($return);
    return $return;
  }

  /**
   * Get the column names in result set
   *
   * @return array
   */
  public function getColumnNames()
  {
    $return = $this->res->getColumnNames();
    MDB2_DAO::validate($return);
    return $return;
  }

  /**
   * Fetch one cell from the result set
   *
   * @return mixed
   */
  public function fetchOne()
  {
    $return = $this->res->fetchOne();
    MDB2_DAO::validate($return);
    return $return;
  }

  /**
   * Fetch one row from the result set
   *
   * @param int $n Row number
   * @return array
   */
  public function fetchRow($n = null)
  {
    $return = $this->res->fetchRow(MDB2_FETCHMODE_ASSOC, $n);

    MDB2_DAO::validate($return);

    if (! empty($return)) {
      $return = $this->toEntity($return);
    }

    return $return;
  }

  /**
   * Alias for fetchCol
   *
   * @param int $n Column number
   * @return mixed
   */
  public function fetchColumn($n = 0)
  {
    return $this->fetchCol($n);
  }

  /**
   * Fetch one column from the result set
   *
   * @param int $n Column number
   * @return mixed
   */
  public function fetchCol($n = 0)
  {
    $return = $this->res->fetchCol($n);

    MDB2_DAO::validate($return);

    return $return;
  }

  /**
   * Fetch all rows and columns from the result set
   *
   * @return array
   */
  public function fetchAll()
  {
    if (empty($this->res)) {
      throw new Exception('No query was executed');
    }

    $return = $this->res->fetchAll();

    MDB2_DAO::validate($return);

    if (! empty($return)) {
      foreach ($return as $key => $row) {
        $return[$key] = $this->toEntity($row);
      }
    }

    return $return;
  }

  /**
   * Build the query from this query object
   *
   * @return unknown_type
   */
  protected function buildQuery()
  {
    $sql = $this->sql;

    if (empty($sql)) {
      $table = $this->getEntity();

      if ($this->getConfig()->hasEntity($table)) {
        $fields = $this->fields;

        if (empty($fields)) {
          $fields = array();

          $conf = $this->getConfig()->getEntity($table);
          foreach ($conf->getFields() as $field => $def) {
            if ($def->getTable()) {
              $fields[] = '' . $def->getTable() . '.' . $def->getName() . '';
            }
            else {
              $fields[] = '{' . $table . '.' . $field . '}';
            }
          }
        }

        if (is_array($fields)) {
          $fields = implode(', ', $fields);
        }

        $entity = $this->getConfig()->getEntity($this->getEntity());

        $table = '{' . $table . '} ' . $entity->join . ' ';
      }
      else {
        $fields = $this->fields;

        if (empty($fields)) {
          $fields = '*';
        }
        elseif (is_array($fields)) {
          $fields = implode(', ', $fields);
        }
      }

      $sql = "SELECT $fields FROM $table ";

      if (! empty($this->joins)) {
        $sql .= implode(' ', $this->joins) . ' ';
      }

      if (! empty($this->where)) {
        $sql .= "WHERE $this->where ";
      }

      if (! empty($this->groupBy)) {
        $sql .= "GROUP BY $this->groupBy ";

        if (! empty($this->having)) {
          $sql .= "HAVING $this->having ";
        }
      }

      $orderBy = trim($this->orderBy);
      if (! empty($orderBy)) {
        $orderBy = implode(', ', ( array ) $orderBy);
        $sql .= "ORDER BY $orderBy ";
      }
    }

    return $sql;
  }

  /**
   * Convert result (associative array) to entity. If Entity unknown return original data.
   *
   * @param array $row query result
   * @return Entity
   */
  protected function toEntity($row)
  {
    $class = $this->getEntity();

    if ($this->getConfig()->hasEntity($class)) {
      $o = DataMapper::toEntity($class, $row, $this->getConfig());
      $o->commit();
      return $o;
    }

    return $row;
  }

}

?>